"""
Historical Figure Manager

This module manages the database of historical figures, their personality data,
biographical information, and provides functionality for dynamic loading and
validation of historical figures.
"""

import json
import logging
from pathlib import Path
from typing import Dict, List, Optional, Any, Set
from datetime import datetime
import hashlib

from ..personality.models import BigFiveTraits


logger = logging.getLogger(__name__)


class HistoricalFigureManager:
    """
    Manages historical figure database and dynamic personality loading.
    
    This class handles:
    - Historical figure database management
    - Dynamic personality loading without system restarts
    - Figure validation and authenticity verification
    - Batch processing of multiple historical figures
    """
    
    def __init__(self, platform):
        """
        Initialize the Historical Figure Manager.
        
        Args:
            platform: Reference to the main AIHistoricalSimulationPlatform
        """
        self.platform = platform
        self.figures_db_path = Path('data/historical_figures')
        self.figures_cache: Dict[str, Dict[str, Any]] = {}
        self.available_figures: Set[str] = set()
        self.figure_categories: Dict[str, List[str]] = {}
        
        # Create data directory if it doesn't exist
        self.figures_db_path.mkdir(parents=True, exist_ok=True)
        
        logger.info("HistoricalFigureManager initialized")
    
    async def initialize(self) -> None:
        """Initialize the figure manager and load figure database."""
        logger.info("Initializing Historical Figure Manager")
        
        try:
            # Load existing figure database
            await self._load_figures_database()
            
            # Initialize with default figures if database is empty
            if not self.available_figures:
                await self._create_default_figures()
            
            logger.info(f"Initialized with {len(self.available_figures)} available figures")
            
        except Exception as e:
            logger.error(f"Failed to initialize figure manager: {e}")
            raise
    
    async def load_figure_data(self, figure_name: str) -> Dict[str, Any]:
        """
        Load complete data for a historical figure.
        
        Args:
            figure_name: Name of the historical figure
            
        Returns:
            Complete figure data including traits, biography, and context
        """
        normalized_name = self._normalize_figure_name(figure_name)
        
        # Check cache first
        if normalized_name in self.figures_cache:
            return self.figures_cache[normalized_name]
        
        # Load from database
        figure_path = self.figures_db_path / f"{normalized_name}.json"
        
        if not figure_path.exists():
            raise ValueError(f"Historical figure '{figure_name}' not found in database")
        
        try:
            with open(figure_path, 'r', encoding='utf-8') as f:
                figure_data = json.load(f)
            
            # Validate figure data
            validated_data = self._validate_figure_data(figure_data)
            
            # Cache the data
            self.figures_cache[normalized_name] = validated_data
            
            logger.info(f"Loaded figure data for: {figure_name}")
            return validated_data
            
        except Exception as e:
            logger.error(f"Failed to load figure data for '{figure_name}': {e}")
            raise
    
    def list_available_figures(self) -> List[str]:
        """Get list of all available historical figures."""
        return sorted(list(self.available_figures))
    
    def get_figures_by_category(self, category: str) -> List[str]:
        """
        Get figures by category.
        
        Args:
            category: Category name (e.g., 'military', 'philosophy', 'science')
            
        Returns:
            List of figure names in the category
        """
        return self.figure_categories.get(category, [])
    
    def get_all_categories(self) -> List[str]:
        """Get all available figure categories."""
        return sorted(list(self.figure_categories.keys()))
    
    async def add_new_figure(self, figure_name: str, figure_data: Dict[str, Any]) -> bool:
        """
        Add a new historical figure to the database.
        
        Args:
            figure_name: Name of the historical figure
            figure_data: Complete figure data
            
        Returns:
            True if successfully added, False otherwise
        """
        try:
            # Validate the figure data
            validated_data = self._validate_figure_data(figure_data)
            
            # Add metadata
            validated_data['metadata'] = {
                'added_date': datetime.now().isoformat(),
                'data_hash': self._calculate_data_hash(validated_data),
                'validation_status': 'validated'
            }
            
            # Save to database
            normalized_name = self._normalize_figure_name(figure_name)
            figure_path = self.figures_db_path / f"{normalized_name}.json"
            
            with open(figure_path, 'w', encoding='utf-8') as f:
                json.dump(validated_data, f, indent=2, ensure_ascii=False)
            
            # Update internal state
            self.available_figures.add(figure_name)
            self.figures_cache[normalized_name] = validated_data
            
            # Update categories
            if 'categories' in validated_data:
                for category in validated_data['categories']:
                    if category not in self.figure_categories:
                        self.figure_categories[category] = []
                    if figure_name not in self.figure_categories[category]:
                        self.figure_categories[category].append(figure_name)
            
            logger.info(f"Successfully added new figure: {figure_name}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to add figure '{figure_name}': {e}")
            return False
    
    async def update_figure(self, figure_name: str, updated_data: Dict[str, Any]) -> bool:
        """
        Update an existing historical figure.
        
        Args:
            figure_name: Name of the historical figure
            updated_data: Updated figure data
            
        Returns:
            True if successfully updated, False otherwise
        """
        try:
            normalized_name = self._normalize_figure_name(figure_name)
            
            # Check if figure exists
            if normalized_name not in self.figures_cache and figure_name not in self.available_figures:
                raise ValueError(f"Figure '{figure_name}' does not exist")
            
            # Load current data
            current_data = await self.load_figure_data(figure_name)
            
            # Merge updates
            merged_data = {**current_data, **updated_data}
            
            # Validate merged data
            validated_data = self._validate_figure_data(merged_data)
            
            # Update metadata
            if 'metadata' not in validated_data:
                validated_data['metadata'] = {}
            validated_data['metadata'].update({
                'last_updated': datetime.now().isoformat(),
                'data_hash': self._calculate_data_hash(validated_data),
                'validation_status': 'validated'
            })
            
            # Save updated data
            figure_path = self.figures_db_path / f"{normalized_name}.json"
            with open(figure_path, 'w', encoding='utf-8') as f:
                json.dump(validated_data, f, indent=2, ensure_ascii=False)
            
            # Update cache
            self.figures_cache[normalized_name] = validated_data
            
            logger.info(f"Successfully updated figure: {figure_name}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to update figure '{figure_name}': {e}")
            return False
    
    async def remove_figure(self, figure_name: str) -> bool:
        """
        Remove a historical figure from the database.
        
        Args:
            figure_name: Name of the historical figure
            
        Returns:
            True if successfully removed, False otherwise
        """
        try:
            normalized_name = self._normalize_figure_name(figure_name)
            
            # Remove from file system
            figure_path = self.figures_db_path / f"{normalized_name}.json"
            if figure_path.exists():
                figure_path.unlink()
            
            # Remove from cache and available figures
            self.figures_cache.pop(normalized_name, None)
            self.available_figures.discard(figure_name)
            
            # Remove from categories
            for category_figures in self.figure_categories.values():
                if figure_name in category_figures:
                    category_figures.remove(figure_name)
            
            logger.info(f"Successfully removed figure: {figure_name}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to remove figure '{figure_name}': {e}")
            return False
    
    def validate_figure_authenticity(self, figure_name: str) -> Dict[str, Any]:
        """
        Validate the authenticity of a historical figure's data.
        
        Args:
            figure_name: Name of the historical figure
            
        Returns:
            Validation report with authenticity metrics
        """
        try:
            normalized_name = self._normalize_figure_name(figure_name)
            
            if normalized_name not in self.figures_cache:
                return {'status': 'error', 'message': 'Figure not found'}
            
            figure_data = self.figures_cache[normalized_name]
            
            validation_report = {
                'figure_name': figure_name,
                'validation_timestamp': datetime.now().isoformat(),
                'authenticity_score': 0.0,
                'checks': {}
            }
            
            # Check biographical data completeness
            bio_completeness = self._check_biographical_completeness(figure_data)
            validation_report['checks']['biographical_completeness'] = bio_completeness
            
            # Check personality trait consistency
            trait_consistency = self._check_trait_consistency(figure_data)
            validation_report['checks']['trait_consistency'] = trait_consistency
            
            # Check historical context accuracy
            context_accuracy = self._check_historical_context(figure_data)
            validation_report['checks']['context_accuracy'] = context_accuracy
            
            # Check data source reliability
            source_reliability = self._check_source_reliability(figure_data)
            validation_report['checks']['source_reliability'] = source_reliability
            
            # Calculate overall authenticity score
            scores = [
                bio_completeness.get('score', 0),
                trait_consistency.get('score', 0),
                context_accuracy.get('score', 0),
                source_reliability.get('score', 0)
            ]
            validation_report['authenticity_score'] = sum(scores) / len(scores)
            
            return validation_report
            
        except Exception as e:
            logger.error(f"Failed to validate figure authenticity for '{figure_name}': {e}")
            return {'status': 'error', 'message': str(e)}
    
    async def batch_process_figures(self, figure_names: List[str], 
                                   operation: str, **kwargs) -> Dict[str, Any]:
        """
        Process multiple figures in batch.
        
        Args:
            figure_names: List of figure names to process
            operation: Operation to perform ('load', 'validate', 'update')
            **kwargs: Additional arguments for the operation
            
        Returns:
            Batch processing results
        """
        logger.info(f"Starting batch processing of {len(figure_names)} figures with operation: {operation}")
        
        results = {
            'successful': [],
            'failed': [],
            'total_processed': 0,
            'operation': operation,
            'timestamp': datetime.now().isoformat()
        }
        
        for figure_name in figure_names:
            try:
                if operation == 'load':
                    await self.load_figure_data(figure_name)
                    results['successful'].append(figure_name)
                    
                elif operation == 'validate':
                    validation_result = self.validate_figure_authenticity(figure_name)
                    if validation_result.get('authenticity_score', 0) > 0.7:
                        results['successful'].append(figure_name)
                    else:
                        results['failed'].append({
                            'figure': figure_name,
                            'reason': 'Low authenticity score',
                            'score': validation_result.get('authenticity_score', 0)
                        })
                
                elif operation == 'update':
                    update_data = kwargs.get('update_data', {})
                    if await self.update_figure(figure_name, update_data):
                        results['successful'].append(figure_name)
                    else:
                        results['failed'].append({
                            'figure': figure_name,
                            'reason': 'Update operation failed'
                        })
                
                results['total_processed'] += 1
                
            except Exception as e:
                results['failed'].append({
                    'figure': figure_name,
                    'reason': str(e)
                })
                results['total_processed'] += 1
        
        logger.info(f"Batch processing complete: {len(results['successful'])} successful, {len(results['failed'])} failed")
        return results
    
    # Private methods
    
    def _normalize_figure_name(self, figure_name: str) -> str:
        """Normalize figure name for consistent file naming."""
        return figure_name.lower().replace(' ', '_').replace('.', '').replace(',', '')
    
    async def _load_figures_database(self) -> None:
        """Load all figures from the database directory."""
        self.available_figures.clear()
        self.figure_categories.clear()
        
        for figure_file in self.figures_db_path.glob('*.json'):
            try:
                with open(figure_file, 'r', encoding='utf-8') as f:
                    figure_data = json.load(f)
                
                figure_name = figure_data.get('name', figure_file.stem.replace('_', ' ').title())
                self.available_figures.add(figure_name)
                
                # Update categories
                if 'categories' in figure_data:
                    for category in figure_data['categories']:
                        if category not in self.figure_categories:
                            self.figure_categories[category] = []
                        if figure_name not in self.figure_categories[category]:
                            self.figure_categories[category].append(figure_name)
                
            except Exception as e:
                logger.warning(f"Failed to load figure from {figure_file}: {e}")
    
    async def _create_default_figures(self) -> None:
        """Create default historical figures for demonstration."""
        default_figures = [
            {
                'name': 'Napoleon Bonaparte',
                'traits': {
                    'openness': 0.8,
                    'conscientiousness': 0.9,
                    'extraversion': 0.7,
                    'agreeableness': 0.3,
                    'neuroticism': 0.4
                },
                'biography': {
                    'birth_year': 1769,
                    'death_year': 1821,
                    'nationality': 'French',
                    'occupation': 'Military leader, Emperor'
                },
                'key_events': [
                    'Rose through military ranks during French Revolution',
                    'Became Emperor of France in 1804',
                    'Led numerous military campaigns across Europe',
                    'Exiled to Elba, returned for Hundred Days',
                    'Final defeat at Battle of Waterloo'
                ],
                'cultural_context': {
                    'power_distance': 0.8,
                    'individualism': 0.6,
                    'uncertainty_avoidance': 0.4
                },
                'historical_context': {
                    'era': 'Early 19th century',
                    'major_events': ['French Revolution', 'Napoleonic Wars'],
                    'social_structure': 'Post-revolutionary France'
                },
                'categories': ['military', 'political', 'european']
            },
            {
                'name': 'Julius Caesar',
                'traits': {
                    'openness': 0.7,
                    'conscientiousness': 0.8,
                    'extraversion': 0.8,
                    'agreeableness': 0.2,
                    'neuroticism': 0.3
                },
                'biography': {
                    'birth_year': -100,
                    'death_year': -44,
                    'nationality': 'Roman',
                    'occupation': 'General, Dictator, Politician'
                },
                'key_events': [
                    'Formed First Triumvirate with Pompey and Crassus',
                    'Conquered Gaul in military campaigns',
                    'Crossed the Rubicon and civil war with Pompey',
                    'Became dictator of Rome',
                    'Assassinated on Ides of March'
                ],
                'cultural_context': {
                    'power_distance': 0.9,
                    'individualism': 0.5,
                    'uncertainty_avoidance': 0.3
                },
                'historical_context': {
                    'era': 'Late Roman Republic',
                    'major_events': ['Gallic Wars', 'Roman Civil War'],
                    'social_structure': 'Roman aristocratic republic'
                },
                'categories': ['military', 'political', 'ancient']
            },
            {
                'name': 'Leonardo da Vinci',
                'traits': {
                    'openness': 0.95,
                    'conscientiousness': 0.7,
                    'extraversion': 0.6,
                    'agreeableness': 0.7,
                    'neuroticism': 0.4
                },
                'biography': {
                    'birth_year': 1452,
                    'death_year': 1519,
                    'nationality': 'Italian',
                    'occupation': 'Artist, Inventor, Scientist'
                },
                'key_events': [
                    'Apprenticed to Andrea del Verrocchio',
                    'Created masterpieces like Mona Lisa and Last Supper',
                    'Designed flying machines and military equipment',
                    'Studied human anatomy and nature',
                    'Served various patrons across Italy and France'
                ],
                'cultural_context': {
                    'power_distance': 0.7,
                    'individualism': 0.7,
                    'uncertainty_avoidance': 0.6
                },
                'historical_context': {
                    'era': 'Renaissance',
                    'major_events': ['Italian Renaissance', 'Age of Discovery'],
                    'social_structure': 'Italian city-states'
                },
                'categories': ['art', 'science', 'renaissance']
            }
        ]
        
        for figure_data in default_figures:
            await self.add_new_figure(figure_data['name'], figure_data)
    
    def _validate_figure_data(self, figure_data: Dict[str, Any]) -> Dict[str, Any]:
        """Validate and normalize figure data."""
        required_fields = ['name', 'traits', 'biography']
        
        for field in required_fields:
            if field not in figure_data:
                raise ValueError(f"Missing required field: {field}")
        
        # Validate personality traits
        if 'traits' in figure_data:
            traits_data = figure_data['traits']
            for trait in ['openness', 'conscientiousness', 'extraversion', 'agreeableness', 'neuroticism']:
                if trait not in traits_data:
                    traits_data[trait] = 0.5  # Default neutral value
                elif not 0 <= traits_data[trait] <= 1:
                    raise ValueError(f"Trait '{trait}' must be between 0 and 1")
        
        return figure_data
    
    def _calculate_data_hash(self, data: Dict[str, Any]) -> str:
        """Calculate hash of figure data for integrity checking."""
        # Remove metadata from hash calculation
        data_copy = {k: v for k, v in data.items() if k != 'metadata'}
        data_str = json.dumps(data_copy, sort_keys=True)
        return hashlib.sha256(data_str.encode()).hexdigest()
    
    def _check_biographical_completeness(self, figure_data: Dict[str, Any]) -> Dict[str, Any]:
        """Check completeness of biographical data."""
        bio_data = figure_data.get('biography', {})
        required_bio_fields = ['birth_year', 'nationality', 'occupation']
        
        present_fields = sum(1 for field in required_bio_fields if field in bio_data)
        completeness_score = present_fields / len(required_bio_fields)
        
        return {
            'score': completeness_score,
            'present_fields': present_fields,
            'total_fields': len(required_bio_fields),
            'missing_fields': [field for field in required_bio_fields if field not in bio_data]
        }
    
    def _check_trait_consistency(self, figure_data: Dict[str, Any]) -> Dict[str, Any]:
        """Check personality trait consistency."""
        traits = figure_data.get('traits', {})
        
        # Check if all big five traits are present
        big_five = ['openness', 'conscientiousness', 'extraversion', 'agreeableness', 'neuroticism']
        present_traits = sum(1 for trait in big_five if trait in traits)
        consistency_score = present_traits / len(big_five)
        
        return {
            'score': consistency_score,
            'present_traits': present_traits,
            'total_traits': len(big_five),
            'trait_ranges_valid': all(0 <= v <= 1 for v in traits.values())
        }
    
    def _check_historical_context(self, figure_data: Dict[str, Any]) -> Dict[str, Any]:
        """Check historical context accuracy."""
        context = figure_data.get('historical_context', {})
        required_context = ['era']
        
        present_context = sum(1 for field in required_context if field in context)
        context_score = present_context / len(required_context) if required_context else 0.8
        
        return {
            'score': context_score,
            'has_era': 'era' in context,
            'has_events': 'major_events' in context,
            'has_social_structure': 'social_structure' in context
        }
    
    def _check_source_reliability(self, figure_data: Dict[str, Any]) -> Dict[str, Any]:
        """Check data source reliability."""
        sources = figure_data.get('sources', [])
        metadata = figure_data.get('metadata', {})
        
        # Simple heuristic: more sources and recent validation = higher reliability
        source_score = min(len(sources) / 3, 1.0) if sources else 0.5
        
        has_validation = 'validation_status' in metadata
        validation_bonus = 0.2 if has_validation and metadata.get('validation_status') == 'validated' else 0
        
        reliability_score = min(source_score + validation_bonus, 1.0)
        
        return {
            'score': reliability_score,
            'source_count': len(sources),
            'has_validation': has_validation,
            'validation_status': metadata.get('validation_status', 'unknown')
        }